import * as Oled from '../lib/oled-ssd1306-i2c';
import * as font from '../lib/oled-font-5x7';
import * as Koa from '../lib/koa';
import * as fs from 'fs';
import * as Router from '../lib/koa-router';
import * as KoaBody from '../lib/koa-body';
import * as cpuStat from 'cpu-stat';
import { v4 as uuidv4 } from '../lib/uuid';
import { Console } from 'console';
import * as moment from 'moment';
const output = fs.createWriteStream('./stdout.log');
const logger = new Console(output);
const app = new Koa();
const router = new Router();
const appList: App[] = [];
const appMap: { [name: string]: App } = {};
start(appList);

app.use(KoaBody());

app.use(async (ctx, next) => {
  const id = uuidv4();
  const beginTimestamp = Date.now();
  logger.log(`[${ moment().format('YYYY-MM-DD HH:mm:ss') }] request [${ id }]: ${ ctx.request.url } body: ${ JSON.stringify(ctx.request.body) } query: ${ ctx.querystring }`);
  await next();
  logger.log(`response[${ id }] (${ (Date.now() - beginTimestamp) }ms) body: ${ ctx.response.body }`);
});

router.post('/update', async (ctx) => {
  const data: App = ctx.request.body;
  if (!data.name || !data.interval || !data.list) {
    ctx.response.body = JSON.stringify(data);
    return;
  }
  for (const item of data.list) {
    if (item.type !== 'text' && item.type !== 'progress') {
      ctx.response.body = 'type error';
      return;
    }
    if (item.type === 'text' && !(item as TextView).text) {
      ctx.response.body = 'not text';
      return;
    }
    if (item.type === 'progress') {
      if (!(item as ProgressView).title) {
        ctx.response.body = 'not text';
        return;
      }
      if (typeof (item as ProgressView).progress !== 'number') {
        ctx.response.body = 'progress is number';
        return;
      }
      if ((item as ProgressView).progress > 1 || (item as ProgressView).progress < 0) {
        ctx.response.body = 'progress error ' + (item as ProgressView).progress;
        return;
      }
    }
  }
  if (appMap[data.name]) {
    Object.assign(appMap[data.name], data);
  } else {
    appMap[data.name] = data;
    appList.push(ctx.request.body);
  }
  if (appMap[data.name].updateFunc) {
    appMap[data.name].updateFunc();
  }
  ctx.response.body = '200';
});

router.get('/remove/:name', async (ctx) => {
  const name = ctx.params.name;
  if (!appMap[name]) {
    ctx.response.body = 'not found name';
    return;
  }
  if (appMap[name].removeFunc) {
    try {
      appMap[name].removeFunc();
    } catch (error) {
      console.log(error);
    }
  }
  delete appMap[name];
  for (let i = 0; i < appList.length; i++) {
    if (appList[i].name === name) {
      appList.splice(i, 1);
      break;
    }
  }
  ctx.response.body = '200';
});

app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
console.log('listening 3000...');
async function start(appList: App[]) {
  const oled = new Oled();
  while (true) {
    try {
      // 清屏
      oled.fillRect(0, 0, 128, 32, 0);
      if (appList.length < 1) {
        await sleep(1000);
        continue;
      }
      const app = appList.shift();
      appList.push(app);
      await updateView(oled, app);
      console.log('555555555555555555555')
    } catch (error) {
      console.error(error);
    }
  }
}

async function updateView(oled, app: App) {
  for (let i = 0; i < app.list.length; i += 2) {
    app.updateFunc = async () => {
      oled.fillRect(0, 0, 128, 32, 0);
      oled.setCursor(0, 0);
      oled.writeString(font, 1, `name: ${ app.name }`, 1, true);
      drawView(oled, 0, 13, app.list[i]);
      if (app.list[i + 1]) { 
        drawView(oled, 0, 25, app.list[i + 1]);
      }
    };
    app.updateFunc();
    let timer: NodeJS.Timeout;
    await Promise.race([
      new Promise((resolve) => {
        timer = setTimeout(resolve, app.interval);
      }),
      new Promise((resolve: () => void) => {
        app.removeFunc = () => {
          clearTimeout(timer);
          resolve();
        };
      })
    ]);
    delete app.updateFunc;
    delete app.removeFunc;
    console.log('-------*********');
  }
}

function drawView(oled, x: number, y: number, view: View) {
  oled.setCursor(x, y);
  switch (view.type) {
    case 'text':
      oled.writeString(font, 1, (view as TextView).text, 1, true);
      break;
    case 'progress':
      const title = `${ (view as ProgressView).title }: `;
      oled.writeString(font, 1, title, 1, true);
      const offsetX = x + title.length * 6 + 2;
      const length = oled.WIDTH - offsetX - 1;
      oled.drawRect(offsetX, y, length, 7, 1, true);
      oled.fillRect(offsetX, y, length * (view as ProgressView).progress, 7, 1, true);
      break;
    default: break;
  }
}

function sleep(interval) {
  return new Promise((resolve) => {
    setTimeout(resolve, interval);
  });
}

(async () => {
  const systemApp: App = {
    name: 'system',
    list: [],
    interval: 2000,
  };
  appList.push(systemApp);
  const temperatureView: TextView = {
    type: 'text',
    text: '',
  };
  const cpuView: ProgressView = {
    type: 'progress',
    title: 'cpu(100%)',
    progress: 0,
  }
  systemApp.list.push(temperatureView, cpuView);
  while (true) {
    // cpu当前温度
    const temperature: number = await new Promise((resolve, reject) => {
      fs.readFile('/sys/class/thermal/thermal_zone0/temp', (err, buffer) => {
        if (err) {
          return reject(err);
        }
        resolve(parseInt(buffer.toString(), 10));
      });
    });
    temperatureView.text = `T: ${ Math.round(temperature / 1000) }°`;
    const cpu: number = await new Promise((resolve, reject) => {
      cpuStat.usagePercent((err: NodeJS.ErrnoException, occupy: number) => {
        if (err) {
          return reject(err);
        }
        resolve(occupy / 100);
      });
    });
    cpuView.title = `cpu(${ Math.round(cpu * 100) }%)`;
    cpuView.progress = cpu;
    await sleep(2000);
  }
})()

interface View {
  type: 'text' | 'progress';
}

interface TextView extends View {
  text: string;
}

interface ProgressView extends View {
  title: string;
  progress: number;
}

interface App {
  // 应用标识
  name: string;
  list: Array<TextView | ProgressView>;
  interval: number;
  updateFunc?: () => Promise<void>;
  removeFunc?: () => void;
}